package n8_设计模式_保护性暂停;

import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import static utils.Sleeper.sleep;

/**
 * ----------------------------------------------------------------------
 * |                                                                    |
 * |     保护性暂停，扩展： 解耦等待 和 生产实现                             |
 * |                                                                   |
 * |=====================================================================
 */
@Slf4j(topic = "c.T3")
public class T3_Guarded_Extend {

    // [t1线程]  等待  [t2线程] 的下载结果
    public static void main(String[] args) {

        // 1. 遍历： 创建 GuardedObject ，最多等5秒 获取内容
        for (int i = 0; i < 3; i++) {
            new People().start();
        }

        // 2. 等待 两秒
        sleep(2);

        // 3. 上面创建好了IDs 遍历， 输入结果
        for (Integer id : Mailboxes.getIDs()){
            new Postman(id, "内容" + id).start();
        }

    }

}
// 居民类
@Slf4j(topic = "c.People")
class People extends Thread{

    @Override
    public void run() {
        // 收信
        final GuardedObject guardedObject = Mailboxes.createGuardedObject();
        log.debug("开始收信 id：{}", guardedObject.getId());

        final Object mail = guardedObject.get(5000);
        log.debug("收到了信 id：{}, 内容：{}", guardedObject.getId(), mail);

    }
}

// 邮递员类
@Slf4j(topic = "c.Postman")
class Postman extends Thread{

    private int id;
    private String mail;

    public Postman(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
        final GuardedObject guardedObject = Mailboxes.getGuardedObject(this.id);
        log.debug("开始送信>>> id：{} , 内容：{}", id, mail);
        guardedObject.complete(this.mail);
    }

}



/*----------------------------------------------
|                                              |
|       解耦 核心方法                           |
|                                             |
|==============================================
邮箱格子 [解耦用] 【解耦： 结果产生线程 和  结果接收的线程 】
*/
class Mailboxes {

    private static Map<Integer, GuardedObject> boxes = new Hashtable<>(); // 线程安全的， 下面put 和 get 都无需加 synchronized

    private static int id = 1;

    // 防止ID 冲突
    private static synchronized int generateId(){
        return id ++;
    }

    // 根据ID 获取信件 【每次取完 得释放，防止堆内存耗尽！】
    public static GuardedObject getGuardedObject(int id){

        return boxes.remove(id); // 获取完 删除
    }

    // 创建对象 并放入集合
    public static GuardedObject createGuardedObject(){
        final GuardedObject go = new GuardedObject(generateId());

        boxes.put(go.getId(), go);
        return go;
    }

    // 获取所有ID
    public static Set<Integer> getIDs(){

        return boxes.keySet();
    }

}


/*---------------------------------------------
|                                             |
|       保护性暂停 核心方法                     |
|                                             |
|=============================================*/
class GuardedObject{

    // 标识 ID
    private int id;

    public GuardedObject(int id){
        this.id = id;
    }

    public int getId() {
        return id;
    }

    // 结果
    private Object response = null;

    // 获取
    public Object get(long timeout){

        synchronized (this){
            // 开始时间
            long begin = System.currentTimeMillis();
            // 经历时间
            long passedTime = 0;

            while(response == null){

                final long surplusWaitTime = timeout - passedTime;  // 还需 等待时间

                // 如果超时，退出
                if(surplusWaitTime <= 0)break;

                try {
                    /*---------------------------------------------------------------------
                    |                                                                     |
                    |     注意：这里wait [不能填写 timeout] ，防止虚假唤醒，重置等待时间        |
                    |                                                                    |
                    |====================================================================*/
                    this.wait(surplusWaitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 计算经历时间
                passedTime = System.currentTimeMillis() - begin;
            }
        }
        return this.response;
    }

    // 产生结果
    public void complete(Object res){

        synchronized (this){

            this.response = res;
            this.notifyAll();
        }

    }


}